"""
This driver has been modified to work with MicroPython

* Author: Walkline Wang
"""

from micropython import const
from utime import sleep, sleep_ms


BAUDRATE_COMMAND = const(250000)   # Speed for command transfers (MUST be slow)
BAUDRATE_DATA    = const(8000000)  # Speed for data transfers (fast!)

_SCI_WRITE_COMMAND = const(0x02)
_SCI_READ_COMMAND  = const(0x03)

_REG_MODE        = const(0x00)
_REG_STATUS      = const(0x01)
_REG_BASS        = const(0x02)
_REG_CLOCKF      = const(0x03)
_REG_DECODE_TIME = const(0x04)
_REG_AUDATA      = const(0x05)
_REG_WRAM        = const(0x06)
_REG_WRAMADDR    = const(0x07)
_REG_HDAT0       = const(0x08)
_REG_HDAT1       = const(0x09)
_REG_VOLUME      = const(0x0B)

# SPI_AIADDR = 0xa
# SPI_AICTRL0 = 0xc
# SPI_AICTRL1 = 0xd
# SPI_AICTRL2 = 0xe
# SPI_AICTRL3 = 0xf

_MODE_SM_DIFF     = const(0x01)
_MODE_SM_JUMP     = const(0x02)
_MODE_SM_RESET    = const(0x04)
_MODE_SM_OUTOFWAV = const(0x08)
_MODE_SM_PDOWN    = const(0x10)
_MODE_SM_TESTS    = const(0x20)
_MODE_SM_STREAM   = const(0x40)
_MODE_SM_PLUSV    = const(0x80)
_MODE_SM_DACT     = const(0x100)
_MODE_SM_SDIORD   = const(0x200)
_MODE_SM_SDISHARE = const(0x400)
_MODE_SM_SDINEW   = const(0x800)
_MODE_SM_ADPCM    = const(0x1000)
_MODE_SM_ADPCM_HP = const(0x2000)


class Player:
	"""
	VS10xx 针脚定义

		1.  GND		地线
		2.  5V		5V 供电，只可以供电
		3.  3.3V	3.3V 供电，当使用 5V 供电时可以输出 3.3V 电压

		4.  XCS		片选输入（低电平有效）
		5.  XDCS	数据片选 / 字节同步
		6.  DREQ	数据请求
		7.  RST		复位引脚（硬复位，低电平有效）

		8.  SCK		SPI 总线时钟线
		9.  SI		SPI 总线数据输入线
		10. SO		SPI 总线数据输出线
	"""

	def __init__(self, spi, xdcs, xcs, dreq, rst):
		self._spi = spi
		self._rst = rst
		self._dreq = dreq
		self._xdcs = xdcs
		self._xcs = xcs

		self.reset()

	def deinit(self):
		self._rst = None
		self._dreq = None
		self._xdcs = None
		self._xcs = None

	def reset(self):
		"""
		Hard reset plus soft reset
		"""

		self._rst.value(0)

		sleep_ms(2)  # It is a must, 2ms

		self._xcs.value(1)
		self._xdcs.value(1)

		self._rst.value(1)

		sleep_ms(2)

		self._wait_for_ready()

		sleep_ms(2)

		self._sci_write(_REG_MODE, _MODE_SM_SDINEW | _MODE_SM_RESET)

		self._sci_write(_REG_HDAT0, 0xABAD)
		self._sci_write(_REG_HDAT1, 0x1DEA)
		sleep_ms(10)

		# Sanity check
		if self._sci_read(_REG_HDAT0) != 0xABAD or self._sci_read(_REG_HDAT1) != 0x1DEA:
			raise RuntimeError("VS10xx Reset failed!")

		self._sci_write(_REG_CLOCKF, 0x9800)  # Set the clock
		# sleep_ms(2)

		self._sci_write(_REG_BASS, 0x0055)  # Set accent
		# sleep_ms(2)

		self._sci_write(_REG_AUDATA, 0xBB81)  # Sample rate 48k, stereo
		# sleep_ms(2)

		self.set_volume(1.0)

		self._spi.write(bytes([0, 0, 0, 0]))

	def _wait_for_ready(self):
		self._dreq.value(1)

		while not self._dreq.value():
			pass

	def _sci_write(self, address, data):
		self._xdcs.value(1)
		self._wait_for_ready()

		self._xcs.value(0)
		self._spi.write(bytes([_SCI_WRITE_COMMAND, address, data >> 8, data & 0xFF]))
		self._xcs.value(1)

	def _sci_read(self, address):
		result = bytearray(4)

		self._xdcs.value(1)
		self._wait_for_ready()
		self._xcs.value(0)
		# self._spi.init(baudrate=BAUDRATE_COMMAND)
		self._spi.write_readinto(bytearray([_SCI_READ_COMMAND, address, 0xff, 0xff]), result)
		self._xcs.value(1)

		return (result[2] << 8) | result[3]

	def _wait_for_low(self):
		while self._dreq.value():
			pass

	def play_chunk(self, buffer):
		self._spi.init(baudrate=BAUDRATE_DATA)
		self._wait_for_ready()
		self._xdcs.value(0)

		try:
			self._spi.write(buffer)
		finally:
			self._xdcs.value(1)

	def set_volume(self, volume):
		""" Sets the volume to the given value (the range is 0 to 1.0).
		Volume is not linear, so values below 0.75 are likely to be too quite to hear.

		Setting the volume to 0 will power down the analog part of the chip, thus saving power.
		"""

		self.set_channel_volume(volume, volume)

	def set_channel_volume(self, left, right):
		""" Sets the volume for each of the channels (left, right). The range is 0 to 1.0 """

		left = max(min(1.0, left), 0)
		right = max(min(1.0, right), 0)
		left_val = round((1 - left) * 255)
		right_val = round((1 - right) * 255)

		self._sci_write(_REG_VOLUME, left_val << 8 | right_val)
		sleep_ms(2)

	@property
	def version(self):
		"""Return the status register version value."""

		return (self._sci_read(_REG_STATUS) >> 4) & 0x0F

	def play_test(self, during=0x44, seconds=2):
		"""Play a sine wave for the specified number of seconds. Useful to
		test the VS1053 is working.
		"""

		self.reset()
		self._sci_write(_REG_MODE, 0x0820)

		self._wait_for_ready()

		try:
			self._xdcs.value(0)
			self._xcs.value(1)
			self._spi.write(bytes([0x53, 0xEF, 0x6E, 0x30, 0x00, 0x00, 0x00, 0x00]))
		finally:
			self._xdcs.value(1)

		sleep(seconds)

		try:
			self._xdcs.value(0)
			self._xcs.value(1)
			self._spi.write(bytes([0x45, 0x78, 0x69, 0x74, 0x00, 0x00, 0x00, 0x00]))
		finally:
			self._xdcs.value(1)
